﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.Statistics.Distribution
{
    /// <summary>
    /// This class represents a Chi-Square distribution.
    /// </summary>
    [Serializable]
    public class ChiSquareDistribution
    {
        /// <summary>
        /// The degree parameter of the distribution.
        /// </summary>
        private readonly int degree;

        /// <summary>
        /// Initializes a new instance of the <see cref="ChiSquareDistribution"/> class.
        /// </summary>
        /// <param name="degree">The degrees of freedom of the distribution.</param>
        public ChiSquareDistribution(int degree)
        {
            if (degree <= 0)
            {
                throw new ArgumentException("degree <= 0");
            }

            this.degree = degree;
        }

        /// <summary>
        /// Gets the degree.
        /// </summary>
        /// <value>The degree.</value>
        public double Degree
        {
            get { return this.degree; }
        }

        /// <summary>
        /// Gets the mean of the distribution.
        /// </summary>
        /// <value>The mean of the distribution.</value>
        public double Mean
        {
            get { return this.degree; }
        }

        /// <summary>
        /// Gets the median of the distribution.
        /// </summary>
        /// <value>The median of the distribution.</value>
        public double Median
        {
            get { return this.degree - 2/3 + 4/27*this.degree - 8/729*this.degree*this.degree; }
        }

        /// <summary>
        /// Gets the mode of the distribution.
        /// </summary>
        /// <value>The mode of the distribution.</value>
        public double Mode
        {
            get
            {
                if (this.degree >= 2)
                {
                    return this.degree - 2;
                }

                return 0;
            }
        }

        /// <summary>
        /// Gets the variance of the distribution.
        /// </summary>
        /// <value>The variance of the distribution.</value>
        public double Variance
        {
            get { return 2*this.degree; }
        }

        /// <summary>
        /// Gets the skewness of the distribution.
        /// </summary>
        /// <value>The skewness of the distribution.</value>
        public double Skewness
        {
            get { return Math.Sqrt(8.0/this.degree); }
        }

        /// <summary>
        /// Computes the probability mass function of the Chi-Square distribution at the specified postion x.
        /// </summary>
        /// <param name="x">The specified position x.</param>
        /// <returns>The probability mass function of the Chi-Square distribution at the specified postion x.</returns>
        public double ProbabilityMassFunction(double x)
        {
            double kHalf = this.degree/2.0;

            return (Math.Pow(0.5, kHalf)/SpecialFunctions.GammaFunction(kHalf))*Math.Pow(x, kHalf - 1)*
                   Math.Pow(Math.E, -x/2);
        }

        /// <summary>
        /// Computes the cumulative distribution function of the Chi-Square distribution at the specified postion k.
        /// </summary>
        /// <param name="k">The specified position k.</param>
        /// <returns>The cumulative distribution function of the Chi-Square distribution at the specified postion k.</returns>
        public double CumulativeDistributionFunction(uint k)
        {
            double a = 0;
            double b = 0;
            double c = 0;
            double kHalf = this.degree/2.0;

            SpecialFunctions.IncompleteGammaFunction(kHalf, kHalf, ref a, ref b, ref c);

            return a/SpecialFunctions.GammaFunction(kHalf);
        }
    }
}